package main

import (
	"http"
	"log"
	"fmt"
	"os"
)

// Define a remote proxy used to respond to request
// if the file is in the staticPath the ostrichd daemon responsds
// if not, sends the request to the given remote server
type ReverseServer struct {
	mux   *http.ServeMux
	proxy *http.ReverseProxy
}

// Create a new reverse proxy pointing to the given remoteServerAddres
// and rooted in the given directory
func NewReverseServer(serverCfg Config) (ret *ReverseServer, err os.Error) {
	ret = nil

	remoteServer, err := serverCfg.GetString("remoteUrl")
	if err != nil {
		return
	}

	url, err := http.ParseURL(remoteServer)
	if err != nil {
		return
	}
	ret = new(ReverseServer)
	ret.proxy = http.NewSingleHostReverseProxy(url)
	ret.mux = http.NewServeMux()

	staticCfg, err := serverCfg.SubCfg("static")
	if err != nil {
		return
	}

	serverCfg.PutIfNone("baseRoot", "./")
	baseRoot, _ := serverCfg.GetString("baseRoot")

	for k, v := range staticCfg {
		log.Printf("Making %q as static path", k)
		log.Printf("Static path is: %q", baseRoot+v.(string))
		log.Printf("Discarding %q prefix", k)
		ret.mux.Handle(k, http.StripPrefix(k, http.FileServer(http.Dir(baseRoot+v.(string)))))
	}

	ret.mux.Handle("/", ret.proxy)
	return
}

// Responds the request
// If the request points to a static resource just sent the desired file
// if not, pass the request to the remoteServer
func (rs *ReverseServer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	rs.mux.ServeHTTP(rw, r)
}


func NewFileServerOnly(cfg Config) http.Handler {
    mux := http.NewServeMux()
    root, _ := cfg.GetString(CFGK_ROOT_FOLDER)
    mux.Handle("/", http.FileServer(http.Dir(root)))

    staticCfg, err := cfg.SubCfg("static")
    if err == nil {
        for k,v := range staticCfg {
            log.Printf("Making %q as static path", k)
            log.Printf("Static path is: %q", root+v.(string))
            log.Printf("Discarding %q prefix", k)
            mux.Handle(k, http.StripPrefix(k, http.FileServer(http.Dir(root+v.(string)))))
        }
    }

    return mux
}

func startHttpServer(exitCode chan int, cfg Config) {

	log.Printf("Configuration: %v\n", cfg)

	// TODO: think about a better way to handle errors here
	port, _ := cfg.GetString(CFGK_PORT)
	fsOnly, _ := cfg.GetBool(CFGK_FS_ONLY)
	https, _ := cfg.GetBool(CFGK_HTTPS)
	var hndl http.Handler
	var err os.Error
	if !fsOnly {
		serverCfg, err := cfg.SubCfg("remoteServer")
		if err != nil {
			log.Printf("Unable to get remote server configuration. Reason: %v\n", err)
			exitCode <- 1
			return
		}

		hndl, err = NewReverseServer(serverCfg)

		if err != nil {
			log.Printf("Unable load reverse server configuration. Reason: %v\n", err)
			exitCode <- 1
			return
		}
	} else {
        hndl = NewFileServerOnly(cfg);
	}

	logHandl := NewLogHandler(hndl)

	log.Printf("Trying to start server on port %v\n", port)

	// no need to prior registration
	// since everything goes to logHandl
	if https {
		cert, _ := cfg.GetString(CFGK_CERT_FILE)
		key, _ := cfg.GetString(CFGK_KEY_FILE)
		err = http.ListenAndServeTLS(port, cert, key, logHandl)
	} else {
		err = http.ListenAndServe(port, logHandl)
	}
	if err != nil {
		log.Printf("Unable to start http server. Reason: %v\n", err)
		exitCode <- 1
	}

	exitCode <- 0
}

type logHandler struct {
	out   chan string
	chain http.Handler
}

func NewLogHandler(chain http.Handler) (ret *logHandler) {
	out := make(chan string, 100)
	ret = &logHandler{out, chain}
	// start loggin everything
	go func() {
		for msg := range out {
			log.Printf("%v\n", msg)
		}
	}()
	return
}

func (l *logHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	l.out <- fmt.Sprintf("%v/%v - %v - from - %v", r.Method, r.Proto, r.RawURL, r.RemoteAddr)
	l.chain.ServeHTTP(w, r)
}
